'\" t
.\"     Title: coap_observe
.\"    Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
.\"      Date: 12/27/2024
.\"    Manual: libcoap Manual
.\"    Source: coap_observe 4.2.0
.\"  Language: English
.\"
.TH "COAP_OBSERVE" "3" "12/27/2024" "coap_observe 4\&.2\&.0" "libcoap Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el       .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
coap_observe, coap_resource_set_get_observable, coap_resource_notify_observers, coap_resource_get_uri_path, coap_find_observer, coap_delete_observer, coap_delete_observers \- work with CoAP observe
.SH "SYNOPSIS"
.sp
\fB#include <coap2/coap\&.h>\fR
.sp
\fBvoid coap_resource_set_get_observable(coap_resource_t *\fR\fB\fIresource\fR\fR\fB, int \fR\fB\fImode\fR\fR\fB);\fR
.sp
\fBint coap_resource_notify_observers(coap_resource_t *\fR\fB\fIresource\fR\fR\fB, const const_string_t *\fR\fB\fIquery\fR\fR\fB);\fR
.sp
\fBconst coap_string_t *coap_resource_get_uri_path(coap_resource_t *\fR\fB\fIresource\fR\fR\fB);\fR
.sp
\fBcoap_subscription_t *coap_find_observer(coap_resource_t *\fR\fB\fIresource\fR\fR\fB, coap_session_t *\fR\fB\fIsession\fR\fR\fB, const const_string_t *\fR\fB\fItoken\fR\fR\fB);\fR
.sp
\fBint coap_delete_observer(coap_resource_t *\fR\fB\fIresource\fR\fR\fB, coap_session_t *\fR\fB\fIsession\fR\fR\fB, const coap_binary_t *\fR\fB\fItoken\fR\fR\fB);\fR
.sp
\fBvoid coap_delete_observers(coap_context_t *\fR\fB\fIcontext\fR\fR\fB, coap_session_t *\fR\fB\fIsession\fR\fR\fB);\fR
.sp
Link with \fB\-lcoap\-2\fR, \fB\-lcoap\-2\-gnutls\fR, \fB\-lcoap\-2\-openssl\fR or \fB\-lcoap\-2\-tinydtls\fR depending on your (D)TLS library type\&.
.SH "DESCRIPTION"
.sp
RFC 7641 extends the CoAP protocol to be able to monitor the state of a resource over time\&.
.sp
This enables clients to "observe" resources with a defined query, i\&.e\&., to retrieve a representation of a resource and keep this representation updated by the server over a period of time\&.
.sp
The server has to flag a resource as "observable", and then the client has to request in a GET request that it wants to observe this resource by the use of the COAP_OPTION_OBSERVE Option with a value of COAP_OBSERVE_ESTABLISH\&. Optionally, the client can specify query options for the resource\&.
.sp
To remove the "observe" subscription, the client has to issue a GET request with the COAP_OPTION_OBSERVE Option with a value of COAP_OBSERVE_CANCEL\&. Alternatively, the server can remove a subscription by calling \fBcoap_delete_observer\fR() or \fBcoap_delete_observers\fR(), but this does not notify the client that the subscription has been removed\&.
.sp
The underlying library adds in and removes "subscribers" to the resource as appropriate in the server side logic\&.
.sp
Within the server application, it needs to determine that there is a change of state of the resource under observation, and then cause the CoAP library layer to initiate a "fake GET request" so that an observe GET response gets sent back to all the clients that are observing the resource\&. The appropriate GET handler within the server application is called to fill in the response packet with the appropriate information\&. This "fake GET request" is triggered by a call to \fBcoap_resource_notify_observers\fR()\&.
.sp
The call to \fBcoap_run_once\fR() in the main server application i/o loop will do all the necessary processing of sending any outstanding "fake GET requests"\&.
.sp
Whenever the server sends a copy of the state of the "observed" resource to the client, it will use the same token used by the client when the client requested the "observe"\&. The client will receive this observe response in the handler defined by \fBcoap_register_response_handler\fR(3)\&. It is the responsibility of the client application to match the supplied token and update the appropriate internal information\&.
.sp
The \fBcoap_resource_set_get_observable\fR() function enables or disables the observable status of the \fIresource\fR by the setting of \fImode\fR\&. If \fImode\fR is 1, then the \fIresource\fR is observable\&. If \fImode\fR is 0, then the \fIresource\fR is no longer observable\&.
.sp
\fBNOTE:\fR It is not possible for the Unknown Resource, created by \fBcoap_resource_unknown_init\fR(3), to be observable as the Uri\-Path is not known when libcoap creates a "fake GET request"\&. The Unknown Resource PUT handler must create a new resource and mark the resource as "observable" if a specific resource needs to be observable\&. The application must then manage the deleteion of the resource at the appropriate time\&.
.sp
\fBNOTE:\fR The type (confirmable or non\-confirmable) of the triggered observe GET response is determined not by the initial GET request, but independently by the server as per RFC 7641 3\&.5\&. Transmission\&. This is controlled by the flags (one of COAP_RESOURCE_FLAGS_NOTIFY_NON or COAP_RESOURCE_FLAGS_NOTIFY_CON) used when creating the resource using \fBcoap_resource_init\fR(3)\&. Furthermore, the server must send at least one "observe" response as confirmable, when generally sending non\-confirmable, every 24 hours\&. libcoap handles this by sending every fifth (COAP_OBS_MAX_NON) response as a confirmable response for detection that the client is still responding\&.
.sp
The \fBcoap_resource_notify_observers\fR() function needs to be called whenever the server application determines that there has been a change to the state of \fIresource\fR, possibly only matching a specific \fIquery\fR if \fIquery\fR is not NULL\&.
.sp
The \fBcoap_resource_get_uri_path\fR() function is used to obtain the UriPath of the \fIresource\fR definion\&.
.sp
The \fBcoap_find_observer\fR() function is used to check whether the current GET request as determined from \fIresource\fR, \fIsession\fR and \fItoken\fR is currently being observed or not\&.
.sp
The \fBcoap_delete_observer\fR() function deletes the specific observer associated with \fIresource\fR, \fIsession\fR and has \fItoken\fR\&.
.sp
The \fBcoap_delete_observers\fR() function is used to delete all observers associated with the \fIsession\fR that is a part of the \fIcontext\fR\&.
.SH "RETURN VALUES"
.sp
The \fBcoap_resource_get_uri_path\fR() function returns the uri_path or NULL if there was a failure\&.
.sp
The \fBcoap_find_observer\fR() function returns the subscription or NULL if there was a failure\&.
.sp
The \fBcoap_resource_set_get_observable\fR() function return 0 on failure, 1 on success\&.
.sp
The \fBcoap_delete_observer\fR() function return 0 on failure, 1 on success\&.
.SH "EXAMPLES"
.sp
\fBSimple Time Server\fR
.sp
.if n \{\
.RS 4
.\}
.nf
#include <coap2/coap\&.h>

coap_resource_t *time_resource = NULL;

static int check_if_time_resource_has_changed(coap_resource_t *resource) {
  return 1;
}

/* specific GET "time" handler, called from hnd_get_generic() */

static void
hnd_get_time(coap_context_t *context, coap_resource_t *resource,
coap_session_t *session, coap_pdu_t *request, coap_string_t *token,
coap_string_t *query, coap_pdu_t *response) {

  unsigned char buf[40];
  size_t len;
  time_t now;

  /* \&.\&.\&. Additional analysis code for resource, request pdu etc\&.  \&.\&.\&. */

  /* After analysis, generate a suitable response */

  /* Note that token, if set, is already in the response pdu */

  now = time(NULL);

  if (query != NULL && coap_string_equal(query, coap_make_str_const("secs"))) {
    /* Output secs since Jan 1 1970 */
    len = snprintf((char *)buf, sizeof(buf), "%lu", now);
  }
  else {
    /* Output human\-readable time */
    struct tm *tmp;
    tmp = gmtime(&now);
    if (!tmp) {
      /* If \*(Aqnow\*(Aq is not valid */
      response\->code = COAP_RESPONSE_CODE(404);
      return;
    }
    len = strftime((char *)buf, sizeof(buf), "%b %d %H:%M:%S", tmp);
  }
  /*
   * Invoke coap_add_data_blocked_response() to do all the hard work\&.
   *
   * Define the format \- COAP_MEDIATYPE_TEXT_PLAIN \- to add in
   * Define how long this response is valid for (secs) \- 1 \- to add in\&.
   *
   * OBSERVE Option added internally if needed within the function
   * BLOCK2 Option added internally if output too large
   * ETAG Option added internally
   */
  coap_add_data_blocked_response(resource, session, request, response, token,
                                 COAP_MEDIATYPE_TEXT_PLAIN, 1,
                                 len,
                                 buf);

  /*
   * As resource\->code has been updated in coap_add_data_blocked_response(),
   * the response pdu will be transmitted by the underlying library\&.
   */

}

/* Generic GET handler */

static void
hnd_get_generic(coap_context_t *ctx, coap_resource_t *resource,
coap_session_t *session, coap_pdu_t *request, coap_string_t *token,
coap_string_t *query, coap_pdu_t *response) {

  coap_str_const_t *uri_path = coap_resource_get_uri_path(resource);

  if (!uri_path) {
    /* Unexpected Failure */
    response\->code = COAP_RESPONSE_CODE(400);
    return;
  }

  /* Is this the "time" resource" ? */
  if (coap_string_equal(uri_path, coap_make_str_const("time"))) {
    hnd_get_time(ctx, resource, session, request, token, query,
                 response);
    return;
  }

  /* Other resources code */

  /* Failure response */
  response\->code = COAP_RESPONSE_CODE(400);
}

/* Initialize generic GET handler */

static void
init_resources(coap_context_t *ctx)
{

  coap_resource_t *r;

  /* Create a resource to return return or update time */
  r = coap_resource_init(coap_make_str_const("time"),
                         COAP_RESOURCE_FLAGS_NOTIFY_CON);

  /* We are using a generic GET handler here */
  coap_register_handler(r, COAP_REQUEST_GET, hnd_get_generic);

  coap_resource_set_get_observable(r, 1);

  coap_add_resource(ctx, r);
  time_resource = r;

}

int main(int argc, char *argv[]){

  coap_context_t *ctx = NULL;
  coap_endpoint_t *ep = NULL;
  coap_address_t addr;
  unsigned wait_ms;

  /* Create the libcoap context */
  ctx = coap_new_context(NULL);
  if (!ctx) {
    exit(1);
  }
  coap_address_init(&addr);
  addr\&.addr\&.sa\&.sa_family = AF_INET;
  addr\&.addr\&.sin\&.sin_port = ntohs(COAP_DEFAULT_PORT);
  ep = coap_new_endpoint(ctx, &addr, COAP_PROTO_UDP);

  /* Other Set up Code */

  init_resources(ctx);

  wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;

  while (1) {
    int result = coap_run_once( ctx, wait_ms );
    if ( result < 0 ) {
      break;
    } else if ( result && (unsigned)result < wait_ms ) {
      /* decrement if there is a result wait time returned */
      wait_ms \-= result;
    } else {
      /*
       * result == 0, or result >= wait_ms
       * (wait_ms could have decremented to a small value, below
       * the granularity of the timer in coap_run_once() and hence
       * result == 0)
       */
      time_t t_now = time(NULL);
      if (t_last != t_now) {
        /* Happens once per second */
        int i;
        t_last = t_now;
        if (time_resource) {
          coap_resource_notify_observers(time_resource, NULL);
        }
      }
      if (result) {
        /* result must have been >= wait_ms, so reset wait_ms */
        wait_ms = COAP_RESOURCE_CHECK_TIME * 1000;
      }
    }
  }
  exit(0);

}
.fi
.if n \{\
.RE
.\}
.sp
\fBClient Observe Request Setup\fR
.sp
.if n \{\
.RS 4
.\}
.nf
#include <coap2/coap\&.h>

/* Usually, requests are sent confirmable */

static unsigned char msgtype = COAP_MESSAGE_CON;

static unsigned int token = 0;

static coap_pdu_t *
coap_new_request(coap_context_t *context, coap_session_t *session, char request_code,
coap_optlist_t **options, unsigned char *data, size_t length, int observe) {

  coap_pdu_t *pdu;
  coap_optlist_t *opt;
  (void)context;

  /* Create the pdu with the appropriate options */
  pdu = coap_pdu_init(msgtype, request_code, coap_new_message_id(session),
                      coap_session_max_pdu_size(session));
  if (!pdu)
    return NULL;

  /*
   * Create uniqueness token for this request for handling unsolicited /
   * delayed responses
   */
  token++;
  if (!coap_add_token(pdu, sizeof(token), (unsigned char*)&token)) {
    coap_log(LOG_DEBUG, "cannot add token to request\en");
    goto error;
  }

  if (request_code == COAP_REQUEST_GET && observe) {
    /* Indicate that we want to observe this resource */
    if (!coap_insert_optlist(options,
                             coap_new_optlist(COAP_OPTION_OBSERVE,
                                         COAP_OBSERVE_ESTABLISH, NULL)))
      goto error;
  }

  /* \&.\&.\&. Other code / options etc\&. \&.\&.\&. */

  /* Add in all the options (after internal sorting) to the pdu */
  if (!coap_add_optlist_pdu(pdu, options))
    goto error;

  if (data && length) {
    /* Add in the specified data */
    if (!coap_add_data(pdu, length, data))
      goto error;
  }

  return pdu;

error:

  coap_delete_pdu(pdu);
  return NULL;

}
.fi
.if n \{\
.RE
.\}
.SH "SEE ALSO"
.sp
\fBcoap_attribute\fR(3), \fBcoap_context\fR(3), \fBcoap_handler\fR(3), \fBcoap_pdu_setup\fR(3) and \fBcoap_resource\fR(3)
.SH "FURTHER INFORMATION"
.sp
"RFC7252: The Constrained Application Protocol (CoAP)" "RFC7641: Observing Resources in the Constrained Application Protocol (CoAP)"
.SH "BUGS"
.sp
Please report bugs on the mailing list for libcoap: libcoap\-developers@lists\&.sourceforge\&.net
.SH "AUTHORS"
.sp
The libcoap project <libcoap\-developers@lists\&.sourceforge\&.net>
